//
//  Projector.m
//  ___PROJECTNAME___
//
//  Created by Goffredo Marocchi on 8/26/10.
//  Copyright 2010 unofficial_sio2_template. All rights reserved.
//

#import "Projector.h"
#import "EAGLView.h"
#import "TextureMatrix.h"

//extern id sio2GLView;

extern BOOL backprojectionFix;
extern BOOL projectorEnable;

@interface Projector ()

-(void) loadTexture:(NSString *)file baseTexture:(BOOL)flag;
-(void) loadProjectorTex;
-(void) loadBackProjectionFixTex;
-(void) loadAttenuationTex;
-(void) prepareSIO2_TEXENV;
-(void) prepareDiffuse0_TEXENV;

@end

@implementation Projector

@synthesize aspectRatio, updateTextureMap;

-(id) init {
	if (self = [super init]) {
		GLint backingWidth;
		GLint backingHeight;
		
		glGenTextures(1, &texture);
		glGenTextures(1, &backprojectionFixTexture);
		glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
		glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
		
		aspectRatio = 1.0f; //we are not using an iPad shaped projector ;).
		updateTextureMap = TRUE;
		
	}
	return self;
}

-(void) dealloc {
	SAFE_FREE(spriteData);
	SAFE_FREE(spriteDataFix);
	SAFE_FREE(spriteDataAtt);
	
	glDeleteTextures(1, &texture);
	glDeleteTextures(1, &backprojectionFixTexture);
	glDeleteTextures(1, &attenuationTexture);
	
	[super dealloc];
}

-(void) setProjector:(NSString*)sio2Projector{
	projector = ( SIO2camera * )sio2ResourceGet( sio2->_SIO2resource,
												SIO2_CAMERA, 
												(char*)[sio2Projector UTF8String]);
	PRINT_SVEC ("projector's origin = ", *projector->_SIO2transform->loc);
}

-(void) calculateProjector {
	//Camera setup for landscape: 
	//order submitted: PROJECTION_MATRIX (Perspective) --> VIEW_MATRIX (lookAt) --> MODEL_MATRIX 
	//order of evaluation: modelToWorld --> worldToCamera (view) --> CameraToHomogeneous/Post-Projection (proj)
	
	//for texture projection: submit BIAS, Projection, Modelview 
	//(PROJECTION and MODELVIEW are calculated from the projector's point of view)
	
	MatrixIdentity(mBias);
	mBias.f[M44_CM(0,0)] = 0.5f;
	mBias.f[M44_CM(1,1)] = 0.5f;
	mBias.f[M44_CM(2,2)] = 0.5f;
	
	mBias.f[M44_CM(0,3)] = 0.5f;
	mBias.f[M44_CM(1,3)] = 0.5f;
	mBias.f[M44_CM(2,3)] = 0.5f;
	
	[[EAGLView sharedGLView]matPerspective:&mProjection.f[0] withFov:projector->fov
							   aspectRatio:1.0f zNear:projector->cstart zFar:projector->cend 
								  portrait:NO];
	
	[[EAGLView sharedGLView]matLookAt:&mView eye:projector->_SIO2transform->loc 
								 vDir:projector->_SIO2transform->dir upVector:(vec3*)&upWorld];
	
	//T = Bias * ProjP * ViewP
	//Oolong's matrix functions operate in row major order
	MatrixIdentity(mTextureProjection);
	MatrixMultiply(mTextureProjection, mProjection, mBias);
	MatrixMultiply(mTextureProjection, mView, mTextureProjection);
	
	float d = 0.0f;
	d = (-1.0f) * sio2DotProduct(projector->_SIO2transform->dir, projector->_SIO2transform->loc);
	
	CMLog (@" (N * P_plane) + D = %f", sio2DotProduct(projector->_SIO2transform->dir, projector->_SIO2transform->loc) + d);
	
	MatrixIdentity(mBackProjectionFix);
	mBackProjectionFix.f[M44_RM(1,1)]=0.0f;
	mBackProjectionFix.f[M44_RM(2,2)]=0.0f;
	
	mBackProjectionFix.f[M44_RM(0,0)]=projector->_SIO2transform->dir->x;
	mBackProjectionFix.f[M44_RM(1,0)]=projector->_SIO2transform->dir->y;
	mBackProjectionFix.f[M44_RM(2,0)]=projector->_SIO2transform->dir->z;
	mBackProjectionFix.f[M44_RM(3,0)]= d;
	
	MatrixIdentity(mBackFacesFix);
	mBackFacesFix.f[M44_RM(1,1)]=0.0f;
	mBackFacesFix.f[M44_RM(2,2)]=0.0f;
	
	mBackFacesFix.f[M44_RM(0,0)]=(-1.0f)*projector->_SIO2transform->dir->x;
	mBackFacesFix.f[M44_RM(1,0)]=(-1.0f)*projector->_SIO2transform->dir->y;
	mBackFacesFix.f[M44_RM(2,0)]=(-1.0f)*projector->_SIO2transform->dir->z;
	mBackFacesFix.f[M44_RM(3,0)]= 0.0f;
	
#if DEBUG_TEST==1
	{
		VECTOR4 vIn;
		VECTOR4 vOut;
		
		vIn.x =projector->_SIO2transform->loc->x;
		vIn.y =projector->_SIO2transform->loc->y;
		vIn.z =projector->_SIO2transform->loc->z;
		vIn.w =1.0f;
		
		MatrixVec4Multiply (vOut, vIn, mFix);
		CMLog(@"s = (BackProjectionFix_Matrix * V_Plane) = %f", vOut.x); //close enough to 0;
		
		vIn.x =1;
		vIn.y =1;
		vIn.z =1;
		vIn.w =1.0f;
		
		MatrixVec4Multiply (vOut, vIn, mFix);
		CMLog(@"s = (BackProjectionFix_Matrix * V(1,1,1)) = %f", vOut.x); 
		
		vIn.x =20;
		vIn.y =40;
		vIn.z =50;
		vIn.w =1.0f;
		
		MatrixVec4Multiply (vOut, vIn, mFix);
		CMLog(@"s = (BackProjectionFix_Matrix * V(20,40,50)) = %f", vOut.x); 
		
		vIn.x =-2;
		vIn.y =-4;
		vIn.z =-5;
		vIn.w =1.0f;
		
		MatrixVec4Multiply (vOut, vIn, mFix);
		CMLog(@"s = (BackProjectionFix_Matrix * V(-2,-4,-5)) = %f", vOut.x); 
	}
#endif
}
-(void) submitProjectorGL {
	glEnable(GL_LIGHTING);
	
	BOOL fixActive = backprojectionFix;
	
	[self loadProjectorTex];
	[self loadAttenuationTex];	
	
	[self prepareSIO2_TEXENV];
	[self prepareDiffuse0_TEXENV];
	
	glActiveTexture(ATTENUATION_TEX);
	//glEnable(GL_TEXTURE_2D);
	
	glActiveTexture(BCULL_TEX);
	glEnable(GL_TEXTURE_2D);
	glActiveTexture(FIX_TEX);
	glEnable(GL_TEXTURE_2D);
	
	glActiveTexture(PROJ_TEX);
	glEnable(GL_TEXTURE_2D);
	
	glActiveTexture(PROJ_TEX);
	glClientActiveTexture(PROJ_TEX);
	
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Plane")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Plane_2")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Plane_3")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Cylinder")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Cylinder_2")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Cube")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere_001")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere_002")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere_002.001")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere_002.002")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere_003")];
	[self sendVPosAsTexcoords:(SIO2object *)sio2ResourceGet(sio2->_SIO2resource, SIO2_OBJECT, (char*)"object/Sphere_004")];
	
	glActiveTexture(PROJ_TEX);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glDisable(GL_TEXTURE_2D);
	
	if(fixActive){
		glActiveTexture(FIX_TEX);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glDisable(GL_TEXTURE_2D);
		
		glActiveTexture(BCULL_TEX);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glDisable(GL_TEXTURE_2D);
		
		glActiveTexture(ATTENUATION_TEX);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glDisable(GL_TEXTURE_2D);
		
		glActiveTexture(DIFFUSE0_TEX);
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glDisable(GL_TEXTURE_2D);
	}
	
	glActiveTexture(PROJ_TEX);
	glClientActiveTexture(PROJ_TEX);
	glBindBuffer(GL_ARRAY_BUFFER, 0);//restores absolute GL pointers
	
	[SysTools checkGLerrors];
}

-(void) loadTexture:(NSString *)file baseTexture:(BOOL)flag {
	CGContextRef spriteContext;
	GLubyte *spriteDataPTR;
	size_t*	widthPTR;
	size_t* heightPTR;
	
	if (flag) {
		spriteDataPTR = spriteData;
		widthPTR = &width;
		heightPTR = &height;
	}
	else {
		spriteDataPTR =  spriteDataFix;
		widthPTR = &widthFix;
		heightPTR = &heightFix;
	}
	
	// Creates a Core Graphics image from an image file
	spriteImage = [UIImage imageNamed:file].CGImage;
	
	if(spriteImage) {
		// Get the width and height of the image
		*widthPTR = CGImageGetWidth(spriteImage);
		*heightPTR = CGImageGetHeight(spriteImage);
		
		// Allocated memory needed for the bitmap context
		SAFE_FREE(spriteDataPTR);
		spriteDataPTR = (GLubyte *) calloc((*widthPTR) * (*heightPTR) * 4, sizeof(GLubyte));
		// Uses the bitmap creation function provided by the Core Graphics framework. 
		spriteContext = CGBitmapContextCreate(spriteDataPTR, *widthPTR, *heightPTR, 8, (*widthPTR) * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
		// After you create the context, you can draw the sprite image to the context.
		
		//y-axis flip (fix)
		CGContextTranslateCTM (spriteContext, 0, *heightPTR);
		CGContextScaleCTM (spriteContext, 1.0, -1.0);
		
		CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat)*widthPTR, (CGFloat)*heightPTR), spriteImage);
		// You don't need the context at this point, so you need to release it to avoid memory leaks.
		CGContextRelease(spriteContext);
	}
	else NSLog(@"(!) File not found... (!)");
	
	// Texture dimensions must be a power of 2. If you write an application that allows users to supply an image,
	// you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2.
	
	if (flag) spriteData = spriteDataPTR;
	else spriteDataFix = spriteDataPTR;
}

-(void) uploadProjectorTex:(NSString *)file {
	[self loadTexture:file baseTexture:YES];
	[self loadTexture:@"BackProjectionFix.png" baseTexture:NO];
}

-(void) uploadAttenuationTex:(NSString *)file {
	CGContextRef spriteContext;
	GLubyte *spriteDataPTR;
	size_t*	widthPTR;
	size_t* heightPTR;
	
	spriteDataPTR = spriteDataAtt;
	widthPTR = &attWidth;
	heightPTR = &attHeight;
	
	// Creates a Core Graphics image from an image file
	spriteImage = [UIImage imageNamed:file].CGImage;
	
	if(spriteImage) {
		// Get the width and height of the image
		*widthPTR = CGImageGetWidth(spriteImage);
		*heightPTR = CGImageGetHeight(spriteImage);
		
		// Allocated memory needed for the bitmap context
		SAFE_FREE(spriteDataPTR);
		spriteDataPTR = (GLubyte *) calloc((*widthPTR) * (*heightPTR) * 4, sizeof(GLubyte));
		// Uses the bitmap creation function provided by the Core Graphics framework. 
		spriteContext = CGBitmapContextCreate(spriteDataPTR, *widthPTR, *heightPTR, 8, (*widthPTR) * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
		// After you create the context, you can draw the sprite image to the context.
		
		//y-axis flip (fix)
		CGContextTranslateCTM (spriteContext, 0, *heightPTR);
		CGContextScaleCTM (spriteContext, 1.0, -1.0);
		
		CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat)*widthPTR, (CGFloat)*heightPTR), spriteImage);
		// You don't need the context at this point, so you need to release it to avoid memory leaks.
		CGContextRelease(spriteContext);
	}
	else NSLog(@"(!) File not found... (!)");
	
	spriteDataAtt = spriteDataPTR;
}

-(void) loadProjectorTex {
	static BOOL loaded = NO;
	
	glActiveTexture(PROJ_TEX);
	glBindTexture(GL_TEXTURE_2D, texture);	
	if (!loaded) {
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		
		//Sample RGB, multiply by previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);   //Modulate RGB with RGB
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		//Sample ALPHA, use previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);  //Modulate ALPHA with ALPHA
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
		
		// Set the texture parameters to use a minifying filter and a linear filer (weighted average)
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		
		//TODO: set TexEnv appropriately in order for multitexturing to enable texture projection to be done in a single pass (additive)
		
		// Specify a 2D texture image, providing the a pointer to the image data in memory
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
		
		loaded = YES;
	}
	
	BOOL fixActive = backprojectionFix;
	if(fixActive) [self loadBackProjectionFixTex];
}

-(void) loadBackProjectionFixTex {
	
	static BOOL loaded = NO;
	
	glActiveTexture(FIX_TEX);
	glBindTexture(GL_TEXTURE_2D, backprojectionFixTexture);
	if (!loaded) {glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		//Sample RGB, multiply by previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);   //Modulate RGB with RGB
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		//Sample ALPHA, use previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);  //Modulate ALPHA with ALPHA
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
		
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		
		// Specify a 2D texture image, providing the a pointer to the image data in memory
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthFix, heightFix, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteDataFix);
		
		//loaded = YES;
	}
	
	glActiveTexture(BCULL_TEX);
	glBindTexture(GL_TEXTURE_2D, backprojectionFixTexture);
	if (!loaded) {glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		//Sample RGB, multiply by previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);   //Modulate RGB with RGB
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		//Sample ALPHA, use previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);  //Modulate ALPHA with ALPHA
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
		
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		
		// Specify a 2D texture image, providing the a pointer to the image data in memory
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthFix, heightFix, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteDataFix);
		
		loaded = YES;
	}
}

-(void) loadAttenuationTex {
	BOOL loaded = NO;
	
	glActiveTexture(ATTENUATION_TEX);
	glBindTexture(GL_TEXTURE_2D, attenuationTexture);
	if (!loaded) {glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		//Sample RGB, multiply by previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);   //Modulate RGB with RGB
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		//Sample ALPHA, use previous texunit result
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);  //Modulate ALPHA with ALPHA
		glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
		
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		
		// Specify a 2D texture image, providing the a pointer to the image data in memory
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, attWidth, attHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteDataAtt);
		
		loaded = YES;
	}
}

-(void) prepareSIO2_TEXENV {
	glActiveTexture(SIO2_TEX0);
	glClientActiveTexture(SIO2_TEX0);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
	glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); //PREVIOUS = (PROJTEX * CLIPTEX * CULLTEX) * ATTENUATION * DIFFUSE0_SIO2
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PRIMARY_COLOR); //PRIMARY_COLOR = VERTEX_LIGHT
	
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_COLOR);
	
	//Sample ALPHA, use previous texunit result
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);  //Modulate ALPHA with ALPHA
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);	
}

-(void) prepareDiffuse0_TEXENV {
	glActiveTexture(DIFFUSE0_TEX);
	glClientActiveTexture(DIFFUSE0_TEX);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
	glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 2);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); //PREVIOUS = PROJTEX * CLIPTEX * CULLTEX * ATTENUATION
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
	
	//Sample ALPHA, use previous texunit result
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);  //Modulate ALPHA with ALPHA
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);	
}

-(void) sendVPosAsTexcoords:(SIO2object*)obj_arg{
	[SysTools checkGLerrors];
	vec3 plane = *projector->_SIO2transform->dir;
	BOOL fixActive = backprojectionFix;
	
	SIO2object* obj = obj_arg;//( SIO2object*)obj_arg->_SIO2instance;
	
	if (VALID_STR(obj_arg->instname)) {
		NSLog(@"name of instance: %s", obj_arg->instname);
		obj = ( SIO2object*)obj_arg->_SIO2instance;
		glBindBuffer(GL_ARRAY_BUFFER, obj->vbo); //following GL client pointers are relative to this VBO
	}
	else {
		glBindBuffer(GL_ARRAY_BUFFER, obj->vbo);
	}
	
	//if( TU0_USED(obj) ) NSLog (@"\n TU0 ......................... \n");
	//if( TU1_USED(obj) ) NSLog (@"\n TU1......................... \n");
	
	if( !TU0_USED(obj) ) {
		glActiveTexture(PROJ_TEX);
		glDisable(GL_TEXTURE_2D);
		
		glActiveTexture(FIX_TEX);
		glDisable(GL_TEXTURE_2D);
		
		glActiveTexture(BCULL_TEX);
		glDisable(GL_TEXTURE_2D);
		
		glActiveTexture(ATTENUATION_TEX);
		glDisable(GL_TEXTURE_2D);
	}
	
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	{
		glActiveTexture(PROJ_TEX);
		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		{
			glLoadMatrixf(mTextureProjection.f);
			
			glTranslatef(obj_arg->_SIO2transform->loc->x, 
						 obj_arg->_SIO2transform->loc->y, 
						 obj_arg->_SIO2transform->loc->z);
			
			glClientActiveTexture(PROJ_TEX);
			glTexCoordPointer(3, GL_FLOAT, 0, (void*) NULL);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			
			if (fixActive) {
				glActiveTexture(FIX_TEX);
				glMatrixMode(GL_TEXTURE);
				glPushMatrix();
				{
					glLoadMatrixf(mBackProjectionFix.f);
					
					glTranslatef(obj_arg->_SIO2transform->loc->x, 
								 obj_arg->_SIO2transform->loc->y, 
								 obj_arg->_SIO2transform->loc->z);
					
					glClientActiveTexture(FIX_TEX);
					glTexCoordPointer(3, GL_FLOAT, 0, (void*) NULL); //vertices are at the beginning of the VBO
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				}
				
				glActiveTexture(BCULL_TEX);
				glMatrixMode(GL_TEXTURE);
				glPushMatrix();
				{
					glLoadMatrixf(mBackFacesFix.f);
					
					glClientActiveTexture(BCULL_TEX);
					glTexCoordPointer(3, GL_FLOAT, 0, SIO2_BUFFER_OFFSET(obj->vbo_offset[ SIO2_OBJECT_NORMALS ]) );
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				}
				
				glActiveTexture(ATTENUATION_TEX);
				glMatrixMode(GL_TEXTURE);
				glPushMatrix();
				{
					glLoadMatrixf(mBackProjectionFix.f);
					
					glTranslatef(obj_arg->_SIO2transform->loc->x, 
								 obj_arg->_SIO2transform->loc->y, 
								 obj_arg->_SIO2transform->loc->z);
					
					glClientActiveTexture(ATTENUATION_TEX);
					glTexCoordPointer(3, GL_FLOAT, 0, (void*) NULL); //vertices are at the beginning of the VBO
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				}
				
				if(TU0_USED(obj)) 
				{glActiveTexture(DIFFUSE0_TEX);
					{
						SIO2material* mat = (SIO2material*)sio2ResourceGet(sio2->_SIO2resource, SIO2_MATERIAL, (char*) "material/Material");
						//sio2MaterialRender(mat);
						
						sio2ImageRender( mat->_SIO2image[ 0 ] );
						
						if( mat->_SIO2image[ 0 ]->_SIO2imagebind )
						{ //mat->_SIO2image[ 0 ]->_SIO2imagebind( mat->_SIO2image[ 0 ], 0 );
						}						
						glEnable(GL_TEXTURE_2D);
						glMatrixMode(GL_TEXTURE);
						glPushMatrix();
						glLoadIdentity();
						
						glClientActiveTexture(DIFFUSE0_TEX);
						
						glTexCoordPointer(2, GL_FLOAT, 0, SIO2_BUFFER_OFFSET(obj->vbo_offset[ SIO2_OBJECT_TEXUV0 ]) );
						glEnableClientState(GL_TEXTURE_COORD_ARRAY);
					}
				}
			}
		}
	}
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	
	if (!obj_arg) {CMLog (@"(!) error, pointer to SIO2 object is NULL(!)");return;}
	
	glActiveTexture(SIO2_TEX0);
	glClientActiveTexture(SIO2_TEX0);
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	int _mask = SIO2_LAMP | SIO2_OBJECT_SOLID;
	if(obj_arg->dst ){
		sio2ObjectRender( obj_arg, sio2->_SIO2window, sio2->_SIO2camera,
						 !( _mask & SIO2_RENDER_NO_MATERIAL ),
						 !( _mask & SIO2_RENDER_NO_MATRIX ) );
	}
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	SIO2_SOFT_RESET();
	
	if(fixActive) {
		glActiveTexture(FIX_TEX);
		glClientActiveTexture(FIX_TEX);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		
		glActiveTexture(BCULL_TEX);
		glClientActiveTexture(BCULL_TEX);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		
		glActiveTexture(ATTENUATION_TEX);
		glClientActiveTexture(ATTENUATION_TEX);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		
		glActiveTexture(DIFFUSE0_TEX);
		glClientActiveTexture(DIFFUSE0_TEX);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		if(TU0_USED(obj)) {
			glMatrixMode(GL_TEXTURE);
			glPopMatrix();
		}
		glDisable(GL_TEXTURE_2D);

	}
	
	glActiveTexture(PROJ_TEX);
	glClientActiveTexture(PROJ_TEX);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glMatrixMode(GL_TEXTURE);
	glPopMatrix();
	
	glMatrixMode(GL_MODELVIEW);
	//glPopMatrix();
}

-(void) rotateZ:(float)angleDeg {
	sio2TransformRotateZ(projector->_SIO2transform, angleDeg);	
}

@end
